import 'dart:math';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_player/video_player.dart';

List<CameraDescription> cameras;
Future<void> main() async {
  var doc = getApplicationDocumentsDirectory();
  print('111111111111$doc 11111111');

  try {
    cameras = await availableCameras();
    print('ccccccccccc $cameras ccccccccccccc');
  } on CameraException catch (e) {
    logError(e.code, e.description);
  }
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    var myDirCur = Directory.current;
    var myDirSys = Directory.systemTemp;
    print('----------current $myDirCur----------');

    print('----------system $myDirSys----------');
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('customWidget'),
        ),
        body: MyHome(),
      ),
    );
  }
}

class MyHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Column(
      children: <Widget>[
        Row(
          children: <Widget>[
            RaisedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (c) {
                    return GBRoute();
                  }),
                );
              },
              child: Text('custom widget'),
            ),
            RaisedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (c) {
                      return TurnBoxRoute();
                    },
                  ),
                );
              },
              child: Text('TurnBox'),
            ),
            RaisedButton(
              onPressed: () {
                Navigator.push(context, MaterialPageRoute(builder: (c) {
                  return CustomPaintRoute();
                }));
              },
              child: Text('chessBoard'),
            ),
          ],
        ),
        RaisedButton(
          child: Text('progress indicator'),
          color: Colors.cyan,
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (c) {
                  return GradientCircularProgressRoute();
                },
              ),
            );
          },
        ),
        RaisedButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (c) {
                  return MyCamera();
                },
              ),
            );
          },
          child: Text('camera'),
          color: Colors.indigo,
        ),
      ],
    );
  }
}

class GradientButton extends StatelessWidget {
  final List<Color> colors;
  final double width;
  final double height;
  final Widget child;
  final GestureTapCallback onTap;

  GradientButton(
      {this.colors, this.width, this.height, this.onTap, @required this.child});
  @override
  Widget build(BuildContext context) {
    ThemeData theme = Theme.of(context);
    //?? if front != null -> back
    List<Color> _colors = colors ??
        [theme.primaryColor, theme.primaryColorDark ?? theme.primaryColor];

    return DecoratedBox(
      decoration: BoxDecoration(
        gradient: LinearGradient(colors: _colors),
      ),
      child: Material(
        type: MaterialType.transparency,
        child: InkWell(
          splashColor: colors.last,
          highlightColor: Colors.transparent,
          onTap: onTap,
          child: ConstrainedBox(
            constraints: BoxConstraints.tightFor(height: height, width: width),
            child: Center(
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: DefaultTextStyle(
                  style: TextStyle(fontWeight: FontWeight.bold),
                  child: child,
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class GBRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[
          GradientButton(
            colors: [Colors.orange, Colors.red],
            height: 50.0,
            child: Text('submit'),
            onTap: onTap,
          ),
          GradientButton(
            colors: [Colors.lightGreen, Colors.green],
            child: Text('submit'),
            height: 50.0,
            onTap: onTap,
          ),
          GradientButton(
            colors: [Colors.lightBlue[300], Colors.blueAccent],
            child: Text('submit'),
            height: 50.0,
            onTap: onTap,
          ),
        ],
      ),
    );
  }

  onTap() {
    print('----click-----');
  }
}

class TurnBoxRoute extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _TurnBoxRouteState();
  }
}

class _TurnBoxRouteState extends State<TurnBoxRoute> {
  double _turns = .0;

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('TurnBox'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            TurnBox(
              turns: _turns,
              speed: 500,
              child: Icon(
                Icons.refresh,
                size: 50,
              ),
            ),
            TurnBox(
              turns: _turns,
              speed: 1000,
              child: Icon(Icons.refresh, size: 150.0),
            ),
            RaisedButton(
              child: Text('clockwise'),
              onPressed: () {
                setState(() {
                  _turns += .2;
                });
              },
            ),
            RaisedButton(
              child: Text('antiClockwise'),
              onPressed: () {
                setState(() {
                  _turns -= .2;
                });
              },
            ),
          ],
        ),
      ),
    );
  }
}

class TurnBox extends StatefulWidget {
  //angel
  final double turns;
  //anime duration
  final int speed;
  final Widget child;

  const TurnBox({Key key, this.turns = .0, this.speed = 200, this.child})
      : super(key: key);
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _TurnBoxState();
  }
}

class _TurnBoxState extends State<TurnBox> with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this, lowerBound: -double.infinity, upperBound: double.infinity);
    _controller.value = widget.turns;
  }

  @override
  void dispose() {
    // TODO: implement dispose
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return RotationTransition(
      turns: _controller,
      child: widget.child,
    );
  }

  @override
  void didUpdateWidget(TurnBox oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
    //angel change meanwhile update anime
    if (oldWidget.turns != widget.turns) {
      _controller.animateTo(
        widget.turns,
        duration: Duration(milliseconds: widget.speed ?? 200),
        curve: Curves.easeOut,
      );
    }
  }
}

class CustomPaintRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child: CustomPaint(
        size: Size(300, 300),
        painter: MyPainter(),
      ),
    );
  }
}

class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint

    double eWidth = size.width / 15;
    double eHeight = size.height / 15;

    var paint = Paint()
      ..isAntiAlias = true
      ..style = PaintingStyle.fill
      ..color = Color(0x77cdb175);
    canvas.drawRect(Offset.zero & size, paint);

    paint
      ..style = PaintingStyle.stroke
      ..color = Colors.black87
      ..strokeWidth = 1.0;

    //chessboard 15row 15 column

    for (var i = 0; i <= 15; i++) {
      //coordinate x -> width
      //           y -> heigh
      double dy = eHeight * i;

      //Offset dynamic type
      canvas.drawLine(Offset(0, dy), Offset(size.width, dy), paint);
    }

    for (var i = 0; i <= 15; i++) {
      double dx = eWidth * i;
      canvas.drawLine(Offset(dx, 0), Offset(dx, size.height), paint);
    }

    //draw chess pieces
    paint
      ..style = PaintingStyle.fill
      ..color = Colors.black;
    canvas.drawCircle(
        //offset position of chess pieces
        Offset(size.width / 8 - eWidth / 2, size.height / 2 - eHeight / 2),
        min(eWidth / 2, eHeight / 2) - 2,
        paint);

    paint
      ..style = PaintingStyle.fill
      ..color = Colors.white;
    canvas.drawCircle(
        Offset(size.width / 2 + eWidth / 2, size.height / 2 - eHeight / 2),
        min(eWidth / 2, eHeight / 2) - 2,
        paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }
}

class GradientCircularProgressRoute extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _GradientCircularProgressRouteState();
  }
}

class _GradientCircularProgressRouteState
    extends State<GradientCircularProgressRoute>
    with SingleTickerProviderStateMixin {
  AnimationController _animeController;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    _animeController = AnimationController(
      vsync: this,
      duration: Duration(seconds: 3),
    );

    bool isForward = true;
    _animeController.addStatusListener((status) {
      if (status == AnimationStatus.forward) {
        isForward = true;
      } else if (status == AnimationStatus.completed ||
          status == AnimationStatus.dismissed) {
        if (isForward) {
          _animeController.reverse();
        } else {
          _animeController.forward();
        }
      } else if (status == AnimationStatus.reverse) {
        isForward = false;
      }
    });
    _animeController.forward();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    _animeController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('CustomProgress'),
      ),
      body: SingleChildScrollView(
        child: Center(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              AnimatedBuilder(
                animation: _animeController,
                builder: ((BuildContext context, Widget child) {
                  return Padding(
                    padding: const EdgeInsets.symmetric(vertical: 16.0),
                    child: Column(
                      children: <Widget>[
                        Wrap(
                          spacing: 10.0,
                          runSpacing: 16.0,
                          children: <Widget>[
                            CustomProssgressIndicator(
                              colors: [Colors.blue, Colors.blue],
                              radius: 55.0,
                              strokeWidth: 3.0,
                              value: _animeController.value,
                            ),
                            CustomProssgressIndicator(
                              colors: [Colors.red, Colors.green],
                              radius: 44.0,
                              strokeWidth: 1.5,
                              value: _animeController.value,
                            ),
                            CustomProssgressIndicator(
                              colors: [Colors.teal, Colors.amber],
                              radius: 33.0,
                              strokeWidth: 2.5,
                              value: _animeController.value,
                            ),
                            CustomProssgressIndicator(
                              colors: [Colors.deepOrange, Colors.cyan],
                              radius: 50.0,
                              strokeWidth: 5.0,
                              strokeCapRound: true,
                              value: CurvedAnimation(
                                parent: _animeController,
                                curve: Curves.decelerate,
                              ).value,
                            ),
                            TurnBox(
                              child: CustomProssgressIndicator(
                                colors: [
                                  Colors.red,
                                  Colors.deepOrange,
                                  Colors.orange
                                ],
                                radius: 45.0,
                                strokeWidth: 4,
                                strokeCapRound: true,
                                totalAngel: 1.5 * pi,
                                value: CurvedAnimation(
                                        parent: _animeController,
                                        curve: Curves.ease)
                                    .value,
                              ),
                              turns: 1 / 8,
                            ),
                            RotatedBox(
                              quarterTurns: 1,
                              child: CustomProssgressIndicator(
                                colors: [Colors.blue[700], Colors.blue[200]],
                                radius: 41.0,
                                strokeWidth: 2.3,
                                strokeCapRound: true,
                                backgroundColor: Colors.transparent,
                                value: _animeController.value,
                              ),
                            ),
                            CustomProssgressIndicator(
                              colors: [
                                Colors.red,
                                Colors.amber,
                                Colors.cyan,
                                Colors.green[200],
                                Colors.blue,
                                Colors.red
                              ],
                              radius: 55.0,
                              strokeWidth: 5.0,
                              strokeCapRound: true,
                              value: _animeController.value,
                            ),
                          ],
                        ),
                        CustomProssgressIndicator(
                          colors: [Colors.blue[700], Colors.blue[200]],
                          radius: 88.0,
                          strokeWidth: 22.0,
                          value: _animeController.value,
                        ),
                        Padding(
                          padding: const EdgeInsets.symmetric(vertical: 15.0),
                          child: CustomProssgressIndicator(
                            colors: [
                              Colors.blue[700],
                              Colors.blue[200],
                            ],
                            radius: 99.0,
                            strokeWidth: 20.0,
                            value: _animeController.value,
                            strokeCapRound: true,
                          ),
                        ),
                        ClipRect(
                          child: Align(
                            alignment: Alignment.topCenter,
                            heightFactor: .5,
                            child: Padding(
                              padding: const EdgeInsets.only(bottom: 8.0),
                              child: SizedBox(
                                //width of sizebox is raidus of child
                                child: TurnBox(
                                  turns: .75,
                                  child: CustomProssgressIndicator(
                                    colors: [Colors.teal, Colors.cyan[500]],
                                    radius: 100.0,
                                    strokeWidth: 9.0,
                                    value: _animeController.value,
                                    totalAngel: pi,
                                    strokeCapRound: true,
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ),
                        SizedBox(
                          height: 104.0,
                          width: 200.0,
                          child: Stack(
                            alignment: Alignment.center,
                            children: <Widget>[
                              //where child of stack positioned
                              Positioned(
                                height: 200.0,
                                //relate top
                                top: .0,
                                child: TurnBox(
                                  turns: .75,
                                  child: CustomProssgressIndicator(
                                    colors: [Colors.teal, Colors.cyan[500]],
                                    radius: 100.0,
                                    strokeWidth: 8.0,
                                    value: _animeController.value,
                                    totalAngel: pi,
                                    strokeCapRound: true,
                                  ),
                                ),
                              ),
                              Padding(
                                padding: const EdgeInsets.only(top: 10.0),
                                child: Text(
                                  '${(_animeController.value * 100).toInt()}%',
                                  style: TextStyle(
                                    fontSize: 25.0,
                                    color: Colors.blueGrey,
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  );
                }),
              )
            ],
          ),
        ),
      ),
    );
  }
}

class CustomProssgressIndicator extends StatelessWidget {
//STORKE
  final double strokeWidth;

  final double radius;

  final bool strokeCapRound;

//position of present animetion
  final double value;

  final Color backgroundColor;

  final double totalAngel;

//gradient colors
  final List<Color> colors;

//where gradient colors stop
  final List<double> stops;

  CustomProssgressIndicator(
      {this.strokeWidth = 2.0,
      @required this.radius,
      @required this.colors,
      this.stops,
      this.strokeCapRound = false,
      this.backgroundColor = const Color(0xFFEEEEEE),
      this.totalAngel = 2 * pi,
      this.value});

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    double _offset = .0;
    if (strokeCapRound) {
      _offset = asin(strokeWidth / (radius * 2 - strokeWidth));
    }
    var _colors = colors;
    if (_colors == null) {
      Color color = Theme.of(context).accentColor;

      _colors = [color, color];
    }
    return Transform.rotate(
      angle: -pi / 2.0 - _offset,
      child: CustomPaint(
        //canvas size
        size: Size.fromRadius(radius),
        painter: _GradientProgressPainter(
          strokeWidth: strokeWidth,
          colors: _colors,
          strokeCapRound: strokeCapRound,
          backgroundColor: backgroundColor,
          value: value,
          totalAngel: totalAngel,
          radius: radius,
        ),
      ),
    );
  }
}

class _GradientProgressPainter extends CustomPainter {
  //STORKE
  final double strokeWidth;

  final double radius;

  final bool strokeCapRound;

//position present
  final double value;

  final Color backgroundColor;

  final double totalAngel;

//gradient colors
  final List<Color> colors;

//where gradient colors stop
  final List<double> stops;

  _GradientProgressPainter(
      {this.strokeWidth = 10.0,
      this.strokeCapRound = false,
      this.backgroundColor = const Color(0xFFEEEEEE),
      this.radius,
      this.totalAngel = 2 * pi,
      @required this.colors,
      this.stops,
      this.value});

  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    if (radius != null) {
      size = Size.fromRadius(radius);
    }
    double _offset = strokeWidth / 2.0;
    double _value = (value ?? .0);
    _value = _value.clamp(.0, 1.0) * totalAngel;
    double _start = .0;
    if (strokeCapRound) {
      _start = asin(strokeWidth / (size.width - strokeWidth));
    }

    Rect rect = Offset(_offset, _offset) &
        Size(size.width - strokeWidth, size.height - strokeWidth);

    var paint = Paint()
      ..strokeCap = strokeCapRound ? StrokeCap.round : StrokeCap.butt
      ..style = PaintingStyle.stroke
      ..isAntiAlias = true
      ..strokeWidth = strokeWidth;

    //draw bg
    if (backgroundColor != Colors.transparent) {
      paint.color = backgroundColor;
      canvas.drawArc(rect, _start, totalAngel, false, paint);
    }

    //draw fg
    if (_value > 0) {
      //sweepgradient extends gradient
      paint.shader = SweepGradient(
        colors: colors,
        startAngle: 0.0,
        endAngle: _value,
        stops: stops,
      ).createShader(rect);

      canvas.drawArc(rect, _start, _value, false, paint);
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    // TODO: implement shouldRepaint
    return true;
  }
}

class MyCameraRoute extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _MyCameraRouteState();
  }
}

class _MyCameraRouteState extends State<MyCameraRoute> {
  @override
  Widget build(BuildContext context) {
    return MyCamera();
  }
}

IconData getCameraLensIcon(CameraLensDirection direction) {
  switch (direction) {
    case CameraLensDirection.back:
      return Icons.camera_rear;
    case CameraLensDirection.front:
      return Icons.camera_front;
    case CameraLensDirection.external:
      return Icons.camera;
  }
  throw ArgumentError('unkonwn lens direction');
}

void logError(String code, String msg) {
  print('Error:$code\n Error Msg:$msg');
}

class MyCamera extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _MyCameraState();
  }
}

class _MyCameraState extends State<MyCamera> with WidgetsBindingObserver {
  CameraController cameraController;
  String imagePath;
  String videoPath;
  VideoPlayerController videoPlayerController;
  //void func(){} name of method
  VoidCallback videoPlayerListener;

  bool enableAudio = true;

  //initialize new camera from description
  void onNewCameraSelected(CameraDescription description) async {
    //
    print('onNewCamera');
    //
    if (cameraController != null) {
      await cameraController.dispose();
    }
    //camera controller initialize
    cameraController = CameraController(
      description,
      ResolutionPreset.high,
      enableAudio: enableAudio,
    );
    //listener when controller has updated -> refresh ui
    cameraController.addListener(() {
      //object insert into Element tree
      // mounted -> update UI
      if (mounted) setState(() {});
      if (cameraController.value.hasError) {
        showInSnackBar(
            'Camera Error ${cameraController.value.errorDescription}');
      }
    });

    try {
      await cameraController.initialize();
    } on CameraException catch (e) {
      _showCameraExcept(e);
    }

    // error has caught update snack bar -> error msg
    if (mounted) {
      setState(() {});
    }
  }

  @override
  void initState() {
    super.initState();
    // with widgetsbindingobserver -> this

    print('initState');
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    print('dispose');
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print('------app life cycle state');
    // TODO: implement didChangeAppLifecycleState
    if (state == AppLifecycleState.inactive) {
      //controller != null -> dispose
      cameraController?.dispose();
    } else if (state == AppLifecycleState.resumed) {
      if (cameraController != null) {
        //define onNew
        onNewCameraSelected(cameraController.description);
      }
    }
  }

  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        title: const Text('Camera'),
      ),
      body: Column(
        children: <Widget>[
          Expanded(
            child: Container(
              child: Padding(
                padding: const EdgeInsets.all(1.0),
                child: Center(
                  child: _cameraPreview(),
                ),
              ),
              //container -> decoration
              decoration: BoxDecoration(
                color: Colors.black,
                border: Border.all(
                  color: cameraController != null &&
                          cameraController.value.isRecordingVideo
                      ? Colors.redAccent
                      : Colors.grey,
                  width: 3.0,
                ),
              ),
            ),
          ),
          _captureControlRowWidget(),
          _toggleAudioWidget(),
          Padding(
            padding: const EdgeInsets.all(5.0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.start,
              children: <Widget>[
                _cameraTogglesRowWidget(),
                _thumbnailWidget(),
              ],
            ),
          )
        ],
      ),
    );
  }

//CameraPreview
//strong code
  Widget _cameraPreview() {
    if (cameraController == null || !cameraController.value.isInitialized) {
      return const Text(
        'tap a camera',
        style: TextStyle(
          color: Colors.white,
          fontSize: 24.0,
          //thikness
          fontWeight: FontWeight.w900,
        ),
      );
    } else {
      return AspectRatio(
        aspectRatio: cameraController.value.aspectRatio,
        child: CameraPreview(cameraController),
      );
    }
  }

//take photo or record video
  Widget _captureControlRowWidget() {
    return Row(
      //same space between widgets in Row
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        IconButton(
          icon: Icon(Icons.camera_alt),
          color: Colors.blue,
          onPressed: cameraController != null &&
                  cameraController.value.isInitialized &&
                  !cameraController.value.isRecordingVideo
              ? onTakePictureButtonPressed
              : null,
        ),
        IconButton(
          icon: const Icon(Icons.videocam),
          color: Colors.blue,
          onPressed: cameraController != null &&
                  cameraController.value.isInitialized &&
                  !cameraController.value.isRecordingVideo
              ? onVideoRecordButtonPressed
              : null,
        ),
        IconButton(
          icon: const Icon(Icons.stop),
          color: Colors.blue,
          onPressed: cameraController != null &&
                  cameraController.value.isInitialized &&
                  cameraController.value.isRecordingVideo
              ? onStopButtonPressed
              : null,
        ),
      ],
    );
  }

  void onStopButtonPressed() {
    stopVideoRecording().then((_) {
      if (mounted) setState(() {});

      //TODO videoPath -> null
      showInSnackBar('video recorded to: $videoPath');
    });
  }

  void onVideoRecordButtonPressed() {
    startVideoRecording().then((String filePath) {
      if (mounted) setState(() {});
      if (filePath != null) showInSnackBar('saving video at $filePath');
    });
  }

  void onTakePictureButtonPressed() {
    //take picture and save photo in filePath
    takePicture().then((String filePath) {
      if (mounted) {
        setState(() {
          imagePath = filePath;
          //after save photo path release resource and set pointer to null
          videoPlayerController?.dispose();
          videoPlayerController = null;
        });
        if (filePath != null) showInSnackBar('photo saved to $filePath');
      }
    });
  }

  //recording audio
  Widget _toggleAudioWidget() {
    return Padding(
      padding: const EdgeInsets.only(left: 25.0),
      child: Row(
        children: <Widget>[
          const Text('enable audio'),
          Switch(
            value: enableAudio,
            onChanged: (bool value) {
              enableAudio = value;
              if (cameraController != null) {
                onNewCameraSelected(cameraController.description);
              }
            },
          ),
        ],
      ),
    );
  }

  Widget _thumbnailWidget() {
    return Expanded(
      child: Align(
        alignment: Alignment.centerRight,
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            // fianlly get VideoPlayer from videoPlayerController controller !=null & has image
            videoPlayerController == null && imagePath == null
                ? Container()
                : SizedBox(
                    child: (videoPlayerController == null)
                        ? Image.file(File(imagePath))
                        : Container(
                            child: Center(
                              child: AspectRatio(
                                aspectRatio: videoPlayerController.value.size !=
                                        null
                                    ? videoPlayerController.value.aspectRatio
                                    : 1.0,
                                child: VideoPlayer(videoPlayerController),
                              ),
                            ),
                            decoration: BoxDecoration(
                              border: Border.all(color: Colors.pink),
                            ),
                          ),
                    //w h of sizedbox
                    width: 64.0,
                    height: 64.0,
                  )
          ],
        ),
      ),
    );
  }

  //select camera
  Widget _cameraTogglesRowWidget() {
    final List<Widget> toggles = <Widget>[];
    print('_cameraToggle $cameras _cameraToggle');

    if (cameras.isEmpty) {
      return const Text('no camera found');
    } else {
      for (CameraDescription description in cameras) {
        print('-----------$description---------------');
        toggles.add(
          SizedBox(
            width: 90.0,
            // radio listtile listtile + radio button
            child: RadioListTile<CameraDescription>(
              title: Icon(
                getCameraLensIcon(description.lensDirection),
              ),
              groupValue: cameraController?.description,
              onChanged: cameraController != null &&
                      cameraController.value.isRecordingVideo
                  ? null
                  : onNewCameraSelected,
              value: description,
            ),
          ),
        );
      }
    }
    return Row(
      //toggles is a <Widget>[]
      children: toggles,
    );
  }

  //time stamp
  // 'unix epoch' 1970-1-1 0:0:0
  String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
  void showInSnackBar(String msg) {
    //global key -> currentState/context(key of object)

    //snackbar -> return scaffoldController
    _scaffoldKey.currentState.showSnackBar(
      SnackBar(
        content: Text(msg),
      ),
    );
  }

  void _showCameraExcept(CameraException e) {
    logError(e.code, e.description);
    showInSnackBar('Error:${e.code}\n${e.description}');
  }

  Future<String> takePicture() async {
    if (!cameraController.value.isInitialized) {
      return null;
    }

    final Directory extDir = await getApplicationDocumentsDirectory();

//set filepath where save photo
    final String dirPath = '${extDir.path}/Pictures/flutter_test';

//create directory
    await Directory(dirPath).create(recursive: true);
//get photo path with extension
    final String filePath = '$dirPath/${timestamp()}.jpg';

//
    if (cameraController.value.isTakingPicture) {
      return null;
    }

    try {
      //cameraController -> takePicture
      //invoke method
      await cameraController.takePicture(filePath);
    } on CameraException catch (e) {
      _showCameraExcept(e);
      return null;
    }
    return filePath;
  }

  Future<String> startVideoRecording() async {
    //cameraController has not initialize -> error msg
    if (!cameraController.value.isInitialized) {
      showInSnackBar('Error:select a camera');
      return null;
    }

    // get directory to save photo
    final Directory extDir = await getApplicationDocumentsDirectory();

    final String dirPath = '${extDir.path}/Movies/flutter _test';

    //recurse to create file and sub file from dirPath
    await Directory(dirPath).create(recursive: true);

    final String filePath = '$dirPath/${timestamp()}.mp4';

    if (cameraController.value.isRecordingVideo) {
      //already recording
      return null;
    }
    try {
      videoPath = filePath;
      //cameraController has method to take photo or recording videeo
      //modify the controller source code to change quality of photo
      await cameraController.startVideoRecording(filePath);
    } on CameraException catch (e) {
      //error occured return null and catch error
      _showCameraExcept(e);
      return null;
    }
    return filePath;
  }

  Future<void> stopVideoRecording() async {
    if (!cameraController.value.isRecordingVideo) {
      return null;
    }

    try {
      await cameraController.stopVideoRecording();
    } on CameraException catch (e) {
      _showCameraExcept(e);
      return null;
    }
    //after record video -> video playback
    await _startVideoPlayer();
  }

  Future<void> _startVideoPlayer() async {
    //create video player controller from a video path

    //create a object doesn't means initialized it
    //create -> alloc memory size
    //initialize -> fill the memory with specs of object
    //configurate vcontroller
    final VideoPlayerController vController =
        VideoPlayerController.file(File(videoPath));

    videoPlayerListener = () {
      //videoPlayerController has been initialized
      if (videoPlayerController != null &&
          videoPlayerController.value.size != null) {
        //refresh the state to update video player with correct ratio
        if (mounted) setState(() {});
        videoPlayerController.removeListener(videoPlayerListener);
      }
    };
    vController.addListener(videoPlayerListener);

//video playback loop
    await vController.setLooping(true);
//initialize
    await vController.initialize();

//  assure videoplayercontroller not null then dispose
    await videoPlayerController?.dispose();
    if (mounted) {
      setState(() {
        imagePath = null;
        //after configurate vContorller -> videoPlayerController
        videoPlayerController = vController;
      });
    }

    //play the video from videoPath(video)
    await vController.play();
  }
}
